import os, sys
sys.path.insert(0, os.path.expanduser('~') + '/lib/python')
from mylib.mygl import *
from mylib.gl3d.vec3 import Vec3


class Camera(object):
    CT_TARGET = 0
    CT_CAMERA = 1
    LIMIT_ANGLE = 89.9

    def __init__(self, _move_type=CT_CAMERA):
        self._distance = 10.0
        self._angle = 45.0
        self._rotate = 0.0
        self._move_type = _move_type
        if self._move_type == self.CT_TARGET:
            self.target = Vec3(0.0, 0.0, 0.0)
            self.camera = Vec3(0.0, 0.0, 0.0)
            self.calc_target_position()
        else:
            self.target = Vec3(0.0, 0.0, 0.0)
            self.camera = Vec3(0.0, 0.0, 0.0)
            self.calc_camera_position()

    def set_angle_limit(self, la): self.LIMIT_ANGLE = la

    def get_view_vector(self):
        if self._move_type == self.CT_TARGET: return self.camera
        else: return self.target

    def get_view_pos(self):
        if self._move_type == self.CT_TARGET: return self.camera
        else: return self.target

    def set_view_pos(self, _x=None, _y=None, _z=None):
        if self._move_type == self.CT_TARGET: self.set_camera(_x, _y, _z)
        else: self.set_target(_x, _y, _z)

    def set_target(self, _x=None, _y=None, _z=None):
        if type(_x) == Vec3: self.target = _x
        else:
            x_ = self.target.x
            y_ = self.target.y
            z_ = self.target.z
            if _x is not None: x_ = _x
            if _y is not None: y_ = _y
            if _z is not None: z_ = _z
            self.target = Vec3(x_, y_, z_)
        self.calc_camera_position()

    def set_camera(self, _x=None, _y=None, _z=None):
        if type(_x) == Vec3: self.camera = _x
        else:
            x_ = self.camera.x
            y_ = self.camera.y
            z_ = self.camera.z
            if _x is not None: x_ = _x
            if _y is not None: y_ = _y
            if _z is not None: z_ = _z
            self.camera = Vec3(x_, y_, z_)
        self.calc_target_position()

    def get_distance(self):
        return self._distance

    def __set_distance(self, value):
        if value < 0.01: value = 0.01
        self._distance = value
        if self._move_type == self.CT_TARGET: self.calc_target_position()
        else: self.calc_camera_position()

    distance = property(get_distance, __set_distance, None, "gets or sets the distance between camera and target")

    def get_angle(self):
        return self._angle

    def __set_angle(self, value):
        if value < 0.01: value = 0.01
        self._angle = value
        if self._move_type == self.CT_TARGET: self.calc_target_position()
        else: self.calc_camera_position()

    angle = property(get_angle, __set_angle, None, "gets or sets the angle")

    def get_rotate(self):
        return self._rotate

    def __set_rotate(self, value):
        if value < 0.01: value = 0.01
        self._rotate = value
        if self._move_type == self.CT_TARGET: self.calc_target_position()
        else: self.calc_camera_position()

    rotate = property(get_rotate, __set_rotate, None, "gets or sets the rotate")

    def change_view(self, _a, _r):
        if self._move_type == self.CT_TARGET:
            self._angle -= _a
            self._rotate += _r
            self.calc_target_position()
        else:
            self._angle += _a
            self._rotate += _r
            self.calc_camera_position()

    def get_position(self, center, rotate=None, angle=None, distance=None):
        if rotate is None: rotate = self._rotate
        if angle is None: angle = self._angle
        if distance is None: distance = self._distance

        _ar = math.radians(angle)
        _rr = math.radians(rotate)
        return Vec3(
            center.x + distance * math.cos(_ar) * math.cos(_rr),
            center.y + distance * math.sin(_ar),
            center.z + distance * math.cos(_ar) * math.sin(_rr)
        )

    def calc_camera_position(self):
        if self.LIMIT_ANGLE < self._angle: self._angle = self.LIMIT_ANGLE
        if -self.LIMIT_ANGLE > self._angle: self._angle = -self.LIMIT_ANGLE
        _, self._rotate = divmod(self._rotate + 3600.0, 360.0)
        self.camera = self.get_position(self.target, self._rotate, self._angle, self._distance)

    def calc_target_position(self):
        if self.LIMIT_ANGLE < self._angle: self._angle = self.LIMIT_ANGLE
        if -self.LIMIT_ANGLE > self._angle: self._angle = -self.LIMIT_ANGLE
        _, self._rotate = divmod(self._rotate + 3600.0, 360.0)
        self.target = self.get_position(self.camera, self._rotate, self._angle, self._distance)

    def move_target(self, _x=None, _y=None, _z=None):
        rad = math.radians(self._rotate)
        if _x is not None:
            self.camera.x += _x * math.sin(rad)
            self.camera.z -= _x * math.cos(rad)
        if _y is not None: self.camera.y += _y
        if _z is not None:
            self.camera.x += _z * math.cos(rad)
            self.camera.z += _z * math.sin(rad)
        self.calc_target_position()

    def move_camera(self, _x=None, _y=None, _z=None):
        rad = math.radians(self._rotate)
        _x = -_x
        _z = -_z
        if _x is not None:
            self.target.x += _x * math.sin(rad)
            self.target.z -= _x * math.cos(rad)
        if _y is not None: self.target.y += _y
        if _z is not None:
            self.target.x += _z * math.cos(rad)
            self.target.z += _z * math.sin(rad)
        self.calc_camera_position()

    def move(self, _x=None, _y=None, _z=None):
        """
        tgt.x -= (math.sin(rad) * dy + math.cos(rad) * dx)
        tgt.z -= (math.cos(rad) * dy - math.sin(rad) * dx)
        """
        if self._move_type == self.CT_TARGET:
            self.move_target(_x, _y, _z)
        else:
            self.move_camera(_x, _y, _z)

    def look(self):
        gluLookAt(
            self.camera.x, self.camera.y, self.camera.z,
            self.target.x, self.target.y, self.target.z,
            0, 1, 0
        )
